import { MyCesiumConsts } from '@/constant';
import ClippingBoxManager from './clipping-box-manager';
import { Cesium3DTileset, Transforms, defined } from "cesium";
import { Cartesian3, Matrix3, Matrix4, PostProcessStageLibrary, Color } from "cesium";

async function loadTileset(url, scene, options, replaceWhenLoading = false, replaceFeatureOptions = {
    isComplex: false,
    tileName: '',
    batchIds: []
}) {
    var fullUrl = url && url != null ? MyCesiumConsts.TILES_BASE + url : undefined;
    if (fullUrl && scene) {
        const targetTileset = await Cesium3DTileset.fromUrl(fullUrl, options);
        // provide a method to replace certain features when loading
        // the method is only used by bunding complex that a tileset includes serval features
        if (replaceWhenLoading && replaceFeatureOptions.isComplex) {
            hideCertainFeaturesWhenLoading(targetTileset, replaceFeatureOptions.tileName, replaceFeatureOptions.batchIds);
        }
        scene.primitives.add(targetTileset);
        return targetTileset;
    }
}

async function loadTilesetWithInitClippingBox(url, scene, options = {
    showBoundingVolume: false,
    isAccessible: false,
    showEdge: false,
    translation: [0, 0, 0],
    showClippingBox: false,
}) {
    var tileset = await loadTileset(url, scene, {
        debugShowBoundingVolume: options.showBoundingVolume
    });
    translate(tileset, ...options.translation);
    tileset.clippingPlanes = ClippingBoxManager.createClippingBoxInitPlanes(options.isAccessible, options.showEdge);
    ClippingBoxManager.adjustClippingBox2SurfaceLevel(tileset, options.isAccessible);
    if (options.showClippingBox) {
        ClippingBoxManager.debugShowClippingBox(tileset);
    }
    return tileset;
}

function translate(tileset, ...transVals) {
    if (defined(tileset) && transVals.length !== 0) {
        const defaultModelCenter = tileset.boundingSphere.center;
        // transform matrix from local to world
        const transforms = Transforms.eastNorthUpToFixedFrame(defaultModelCenter);
        // create translation matrix(3x3)
        const translations = Cartesian3.fromArray(transVals);
        // calc new vector of new position under world
        const offsetVec = Matrix4.multiplyByPoint(transforms, translations, new Cartesian3());
        // calc actual translation vector(distance) under world
        const translationVec = Cartesian3.subtract(offsetVec, defaultModelCenter, new Cartesian3());
        // turn 3x3 matrix to 4x4
        tileset.modelMatrix = Matrix4.fromTranslation(translationVec);
        return tileset.modelMatrix;
    }
}

function rotate(tileset, degree) {
    if (defined(tileset)) {
        const defaultModelCenter = tileset.boundingSphere.center;
        // transform matrix from local to world
        const transforms = Transforms.eastNorthUpToFixedFrame(defaultModelCenter);
        const rotates = Matrix3.fromRotationZ(Math.toRadians(degree));
        const rotateX = Matrix4.fromRotation(rotates);
        const newModelMatrix = Matrix4.multiplyByMatrix3(transforms, rotateX, new Matrix4());
        tileset.modelMatrix = newModelMatrix;
    }
}

function hideCertainFeaturesWhenLoading(tileset, tileName, batchIds) {
    if (defined(tileset) && tileName.length !== 0 && batchIds.length !== 0) {
        // listen the loading process of every tile(model file)
        // callback every time when loading and get a Cesium3dTile Object(a tile file)
        tileset.tileLoad.addEventListener((tile) => {
            console.log(tile);
            setFeatureShowState(tile, tileName, batchIds);
        });
    }
}

function replaceFeaturesAfterLoad(tileset, tileName = '', batchIds = [], showState = false) {
    if (defined(tileset) && tileName.length !== 0 && batchIds.length !== 0) {
        const tiles = tileset.root.children;
        tiles.forEach(tile => {
            setFeatureShowState(tile, tileName, batchIds, showState);
        });
    }
}

function restoreFeaturesAfterLoad(tileset, tileName = '', batchIds = []) {
    replaceFeaturesAfterLoad(tileset, tileName, batchIds, true);
}

function setFeatureShowState(tile, targetTileName, batchIds, showState = false) {
    // find the target tile
    const _tileName = tile._contentHeader.uri;
    if (_tileName && _tileName === targetTileName) {
        // get Cesium3DTileContent Object
        const _content = tile.content;
        batchIds.forEach(batchId => {
            // get Cesium3DTileFeature Object
            const _feature = _content.getFeature(batchId);
            if (_feature) {
                _feature.show = showState;
            }
        });    
    }
}

function renderSelectedFeature(tileset, tileMap = {}) {
    const viewer = window.viewer.viewerInstance;
    const scene = viewer.scene;
    if (defined(tileset)) {
        // need to open the cesium function of terrainAgainst
        if (PostProcessStageLibrary.isSilhouetteSupported(scene)) {
            var highlightFeatures = [];
            const silhouetteBlue = PostProcessStageLibrary.createEdgeDetectionStage();
            silhouetteBlue.uniforms.color = Color.LIME;
            silhouetteBlue.uniforms.length = 0.01;
            silhouetteBlue.selected = [];
            scene.postProcessStages.removeAll();
            scene.postProcessStages.add(
                PostProcessStageLibrary.createSilhouetteStage([silhouetteBlue])
            );
            const tiles = tileset.root.children;
            tiles.forEach(tile => {
                const _tileName = tile._contentHeader.uri;
                if (tileMap[_tileName]) {
                    const batchIds = tileMap[_tileName];
                    const _content = tile.content;
                    batchIds.forEach(batchId => {
                        const targetFeature = _content.getFeature(batchId);
                        if (targetFeature) {
                            highlightFeatures.push(targetFeature);
                        }
                    });
                }
            });
            silhouetteBlue.selected = highlightFeatures;
        }
    }
}

export default {
    loadTileset,
    loadTilesetWithInitClippingBox,
    translate,
    rotate,
    hideCertainFeaturesWhenLoading,
    replaceFeaturesAfterLoad,
    renderSelectedFeature,
    restoreFeaturesAfterLoad,
}